Analysis of RNA-seq data

Four BRAF-mutant melanoma cell lines (A375, SKMEL28, SKMEL5 and WM88) in standard culture medium (naive) or treated with BRAFi (8µM PLX4720) for 8 days (tolerant). Illumina Novaseq paired end reads.

library(DESeq2)
Loading required package: S4Vectors
Loading required package: stats4
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname, do.call,
    duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map,
    mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind,
    Reduce, rownames, sapply, setdiff, table, tapply, union, unique, unsplit, which.max, which.min


Attaching package: ‘S4Vectors’

The following object is masked from ‘package:utils’:

    findMatches

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats

Attaching package: ‘MatrixGenerics’

The following objects are masked from ‘package:matrixStats’:

    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts, colCummaxs, colCummins,
    colCumprods, colCumsums, colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs, colMads,
    colMaxs, colMeans2, colMedians, colMins, colOrderStats, colProds, colQuantiles, colRanges,
    colRanks, colSdDiffs, colSds, colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
    colWeightedMeans, colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs,
    rowAnys, rowAvgsPerColSet, rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps, rowMadDiffs, rowMads, rowMaxs,
    rowMeans2, rowMedians, rowMins, rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
    rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars, rowWeightedMads,
    rowWeightedMeans, rowWeightedMedians, rowWeightedSds, rowWeightedVars

Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor,
    see 'citation("Biobase")', and for packages 'citation("pkgname")'.


Attaching package: ‘Biobase’

The following object is masked from ‘package:MatrixGenerics’:

    rowMedians

The following objects are masked from ‘package:matrixStats’:

    anyMissing, rowMedians
library(EnhancedVolcano)
Loading required package: ggplot2
Loading required package: ggrepel
library(org.Hs.eg.db)
Loading required package: AnnotationDbi
library(ggplot2)

OVERWRITE <- FALSE
OUTPUT_DIR <- file.path("~/P2RX7_ms_output")
if(!dir.exists(OUTPUT_DIR)) dir.create(OUTPUT_DIR)
d <- read.csv("../data/STARquantMode_genecounts_all.csv", row.names = 1)
genes <- rownames(d)

samples <- colnames(d)
cell_lines <- factor(sapply(strsplit(samples, "_"), "[[", 1))
conds <- factor(ifelse(sapply(strsplit(samples, "_"), "[[", 2)=="8day","tolerant","naive"))
reps <- factor(gsub("rep","",sapply(strsplit(samples, "_"), "[[", 3)))
coldat <- data.frame(cell_line=cell_lines,condition=conds,rep=reps)
coldat$group <- factor(paste(coldat$cell_line,coldat$condition,sep="_"))

lowgenes <- rownames(d[apply(d,1,function(x) all(x<10)),])

Make DESeq dataset

dds <- DESeqDataSetFromMatrix(countData = d,
                              colData = coldat,
                              design = ~ cell_line + condition)

Set reference condition

Using drug-tolerant A375 cells as reference.

Maintaining low-expressed genes reduces the impact of cell line-specific differences in gene expression.

dds$cell_line <- relevel(dds$cell_line, ref = "A375")
dds$condition <- relevel(dds$condition, ref = "tolerant")

dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing

Normalized counts

dds <- estimateSizeFactors(dds)
normalized_counts <- counts(dds, normalized=TRUE)

Variance stabilization for PCA

res <- results(dds)
vsd <- vst(dds, blind=FALSE)

% of genes that are both is.na(padj) and in lowgenes

Missing values for padj are highly enriched in low-expressed genes.

cat(paste(signif(length(which(rownames(res)[which(is.na(res$padj))] %in% lowgenes))/length(rownames(res)[which(is.na(res$padj))])*100,4),"% of genes with padj == NA are in lowgenes"))
98.04 % of genes with padj == NA are in lowgenes

PCA of variance-stabilized data

pcaData <- plotPCA(vsd, intgroup=c("condition", "cell_line"), returnData=TRUE)
using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar"))
ggplot(pcaData, aes(PC1, PC2, color=condition, shape=cell_line)) +
  geom_point(size=3) +
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance")) + 
  coord_fixed()

Specifically examine tolerant vs naive

Ignoring cell line-specific differences.

res_tolerant <- results(dds, contrast=c("condition","tolerant","naive"))
res_tolerant <- res_tolerant[order(res_tolerant$padj),]
res_tolerant <- res_tolerant[!is.na(res_tolerant$padj),]
summary(res_tolerant)

out of 27563 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 7252, 26%
LFC < 0 (down)     : 5785, 21%
outliers [1]       : 0, 0%
low counts [2]     : 0, 0%
(mean count < 1)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Volcano plot of differential gene expression

ggp <- EnhancedVolcano(res_tolerant,
    lab = rownames(res_tolerant),
    x = 'log2FoldChange',
    y = 'pvalue',
    pCutoff = 1e-03,
    pCutoffCol = "padj",
    title = "Drug-tolerant vs naive",
    subtitle = bquote(italic("all samples, all genes")),
    axisLabSize=9,
    labSize=1.5, legendLabSize=7, legendIconSize=1, pointSize=1,
    titleLabSize=12, subtitleLabSize=10, captionLabSize=10,
    shape = 20,
    col = c("grey30", "grey60", "orange", "red2"),
    max.overlaps = 20,
    borderWidth = 0.6,
    encircleSize = 1
    )
ggp

mylabels <- c("ABCB5","MERTK","NGFR","AXL","EGFR","TYRP1","DCT","EDNRB","PPARGC1A",
              "TRPM1","TRPM8","P2RX4","P2RX7","CACNA1C","PANX2","CACNA1D",
              "TRPC4","NFATC4",
              "SLC24A2","SPRY4","EREG","MITF",
              "ETV5","ETV4","SPRY1",
              "CCND2","CXCL12")

Supplementary Figure S7

ggp <- EnhancedVolcano(res_tolerant,
    lab = rownames(res_tolerant),
    selectLab = mylabels,
    x = 'log2FoldChange',
    y = 'pvalue',
    xlim=c(-6,9),
    ylim=c(0,75),    
    pCutoff = 1e-03,
    pCutoffCol = "padj",
    title = "Drug-tolerant vs naive",
    subtitle = bquote(italic("all samples")),
    axisLabSize=9,
    labSize=1.25, legendLabSize=7, legendIconSize=1, pointSize=1,
    titleLabSize=12, subtitleLabSize=10, captionLabSize=10,
    shape = 20,
    col = c("grey30", "grey60", "orange", "red2"),
    drawConnectors = TRUE,
    widthConnectors = .5,
    typeConnectors = "closed",
    endsConnectors = "last",
    boxedLabels = TRUE,
    max.overlaps = 20,
    borderWidth = 0.6,
    encircleSize = 1
    )
ggp

fn <- file.path(OUTPUT_DIR,"Volcano_avg.pdf")
if(OVERWRITE | !file.exists(fn))
  ggsave(file=fn, device="pdf", width=4, height=5, units="in")

Gene enrichment ontology analysis

Using Benjamini-Hochberg-adjusted p-value of 0.05 and 1.5-fold change of expression as cutoffs.

upreg_genes <- subset(res_tolerant, padj<0.05 & log2FoldChange>0.5849625)
downreg_genes <-subset(res_tolerant, padj<0.05 & log2FoldChange<(-0.5849625))

geneList_up <- as.vector(upreg_genes$log2FoldChange)
names(geneList_up) <- rownames(upreg_genes)
geneList_down <- as.vector(downreg_genes$log2FoldChange)
names(geneList_down) <- rownames(downreg_genes)

genes_up <- as.vector(rownames(upreg_genes))
genes_down <- as.vector(rownames(downreg_genes))

Using clusterProfiler to perform gene ontology enrichment analysis

https://bioconductor.org/packages/release/bioc/vignettes/clusterProfiler/inst/doc/clusterProfiler.html

ggo_up <- clusterProfiler::groupGO(gene  = genes_up,
                                OrgDb    = org.Hs.eg.db,
                                keyType  = "SYMBOL",
                                ont      = "MF",
                                level    = 3,
                                readable = TRUE)
ggo_up <- as.data.frame(ggo_up)
ggo_up <- ggo_up[order(-ggo_up$Count),] 
ego_genesUp <- clusterProfiler::enrichGO(gene  = genes_up,
                                 OrgDb         = org.Hs.eg.db,
                                 keyType  = "SYMBOL",
                                 ont           = "MF",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)
clusterProfiler::dotplot(ego_genesUp) + 
  ggtitle("GO Over-representation Upregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 8),
        plot.title = element_text(size = 12, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=8),
        legend.title = element_text(size=8,face="bold"), 
        axis.title=element_text(size=9, face="bold"))


fn <- file.path(OUTPUT_DIR,"GO_upreg.pdf")
if(OVERWRITE | !file.exists(fn))
  ggsave(file=fn, device="pdf", width=6, height=4, units="in")
ggo_down <- clusterProfiler::groupGO(gene  = genes_down,
                                OrgDb    = org.Hs.eg.db,
                                keyType  = "SYMBOL",
                                ont      = "MF",
                                level    = 3,
                                readable = TRUE)
ggo_down <- as.data.frame(ggo_down)
ggo_down <- ggo_down[order(-ggo_down$Count),] 
ego_genesDown <- clusterProfiler::enrichGO(gene  = genes_down,
                                 OrgDb         = org.Hs.eg.db,
                                 keyType       = "SYMBOL",
                                 ont           = "MF",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)
clusterProfiler::dotplot(ego_genesDown) + 
  ggtitle("GO Over-representation Downregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 8),
        plot.title = element_text(size = 12, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=8),
        legend.title = element_text(size=8,face="bold"), 
        axis.title=element_text(size=9, face="bold"))

upgenes_filtered <- as.data.frame(upreg_genes[order(upreg_genes$log2FoldChange,
                                                    decreasing=TRUE),])

upgenes_filtered <- subset(upgenes_filtered, log2FoldChange>1.5 & 
                             padj < 1e-4 & baseMean > 200)

### Exclude LINC (long noncoding RNAs) and LOC (unannotated) genes
upgenes_filtered <- subset(upgenes_filtered, !grepl("^L[OI][CN]",rownames(upgenes_filtered)))

upgenes_filtered

Calcium ion transport gene ontologies:
https://www.informatics.jax.org/vocab/gene_ontology/GO:0006816

calcium ion transmembrane transport
GO:0070588

Amino acid transporter GO:0006865

monoatomic cation channel activity GO:0005261

go_enriched2 <- strsplit(ego_genesUp@result["GO:0005261","geneID"],"/")
names(go_enriched2) <- "GO:0005261"

go_labels <- unlist(go_enriched2[[1]])
x <- res_tolerant[go_labels,c("log2FoldChange","padj")]
x <- x[order(x$log2FoldChange, decreasing=TRUE),]
go_labels <- rownames(x)

go_labels2 <- c("KCND3","KCNQ3","KCNB1","P2RX7","KCNJ13","P2RX1","TMEM63C","CACNA1D","CACNA1C")

Supplementary Figure S8

ggp <- EnhancedVolcano(res_tolerant,
    lab = rownames(res_tolerant),
    selectLab = go_labels,
    title = "Genes associated with monoatomic cation channel activity",
    subtitle = bquote(italic(GO:0005261)),
    caption = paste0("total = ", nrow(res_tolerant), " genes"),
    x = 'log2FoldChange',
    y = 'pvalue',
    pCutoff = 1e-02,
    pCutoffCol = "padj",
    xlim=c(-1,6),
    ylim=c(0,60),
    axisLabSize=9,
    labSize=1.25, legendLabSize=7, legendIconSize=1, pointSize=1,
    titleLabSize=12, subtitleLabSize=10, captionLabSize=10,
    shape = 20,
    col = c("grey30", "grey60", "orange", "red2"),
    drawConnectors = TRUE,
    widthConnectors = .5,
    typeConnectors = "closed",
    endsConnectors = "last",
    boxedLabels = TRUE,
    max.overlaps = 20,
    borderWidth = 0.6,
    encircleSize = 1
    )
ggp


fn <- file.path(OUTPUT_DIR,"Volcano_avg_GO-5261.pdf")
if(OVERWRITE | !file.exists(fn))
  ggsave(file=fn, device="pdf", width=6, height=4, units="in")

Plotting function for gene expression levels across cell line data (drug-tolerant vs naive)

plot_naive_v_tol <- function(gene, yl=c(-2,18)){
  gene <- gene[1] # use only first item
  x <- cbind(coldat,gene=as.integer(normalized_counts[gene,]))
  g <- ggplot(x, aes(cell_line, log2(gene), color=condition, shape=condition)) +
    geom_jitter(width = .075, size=2) + 
    theme_classic() + 
    theme(legend.position = "top", legend.direction = "horizontal") + 
    theme(legend.title = element_text(size=0), legend.text = element_text(size=8)) + 
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) + 
    labs(y="log2(normalized counts)", x="", title=gene) + 
    scale_y_continuous(limits=yl, breaks=seq(yl[1],yl[2],2))
  g
}
plot_naive_v_tol("ATP1A1", yl=c(14,19))

plot_naive_v_tol("P2RX7")

downreg_genes["ITPR2",]
log2 fold change (MLE): condition tolerant vs naive 
Wald test p-value: condition tolerant vs naive 
DataFrame with 1 row and 6 columns
       baseMean log2FoldChange     lfcSE      stat      pvalue        padj
      <numeric>      <numeric> <numeric> <numeric>   <numeric>   <numeric>
ITPR2    1865.5       -1.26249  0.245782  -5.13663 2.79704e-07 2.60808e-06

Supplementary Figure S6

plot_naive_v_tol("ITPR2", yl=c(8,13))

plot_naive_v_tol("ITPR3",yl=c(11,15))

downreg_genes["CALB2",]
log2 fold change (MLE): condition tolerant vs naive 
Wald test p-value: condition tolerant vs naive 
DataFrame with 1 row and 6 columns
       baseMean log2FoldChange     lfcSE      stat      pvalue        padj
      <numeric>      <numeric> <numeric> <numeric>   <numeric>   <numeric>
CALB2   222.184        -4.7774  0.779059  -6.13227 8.66344e-10 1.39562e-08
p <- list(plot_naive_v_tol("CACNA1C",yl=c(-2,8)),
          plot_naive_v_tol("CACNA1D",yl=c(2,10)),
          plot_naive_v_tol("P2RX4",yl=c(4,14)),
          plot_naive_v_tol("P2RX1", yl=c(-2,8)),
          plot_naive_v_tol("P2RX7", yl=c(2,16)),
          plot_naive_v_tol("ITPR2",yl=c(8,13)),
          plot_naive_v_tol("CALB2", yl=c(-2,12)),
          plot_naive_v_tol("ATP2B2", yl=c(-2,8)),
          plot_naive_v_tol("NGFR", yl=c(-2,18)))
p
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

[[9]]

Melanoblast genes

Thomas, A. J. & Erickson, C. A. The making of a melanocyte: the specification of melanoblasts from the neural crest. Pigment Cell Melanoma Res 21, 598–610 (2008).

NOTE: all are high for SOX10, PAX3 and MITF

# neural_crest_genes <- c('NGFR', 'AQP1', 'GFRA2', 'L1CAM', 'SLITRK6', 'RXRG','SOX10')
melanoblast_genes <- c('SOX10','MITF','PAX3','FOXD3','KIT','DCT','EDNRB','TYRP1')

p <- lapply(melanoblast_genes, function(x) plot_naive_v_tol(x, yl=c(-4,20)))
p
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

LS0tCnRpdGxlOiAiRHJ1Zy10b2xlcmFudCBtZWxhbm9tYSBSTkFzZXEgYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IERhcnJlbiBUeXNvbiAmIFBoaWxpcCBTdGF1ZmZlcgpkYXRlOiAyMDIzLTA4LTI5Ci0tLQojIEFuYWx5c2lzIG9mIFJOQS1zZXEgZGF0YQpGb3VyIEJSQUYtbXV0YW50IG1lbGFub21hIGNlbGwgbGluZXMgKEEzNzUsIFNLTUVMMjgsIFNLTUVMNSBhbmQgV004OCkgaW4gc3RhbmRhcmQgY3VsdHVyZSBtZWRpdW0gKG5haXZlKSBvciB0cmVhdGVkIHdpdGggQlJBRmkgKDjCtU0gUExYNDcyMCkgZm9yIDggZGF5cyAodG9sZXJhbnQpLiBJbGx1bWluYSBOb3Zhc2VxIHBhaXJlZCBlbmQgcmVhZHMuCgoKYGBge3IgU2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkoZ2dwbG90MikKCk9WRVJXUklURSA8LSBGQUxTRQpPVVRQVVRfRElSIDwtIGZpbGUucGF0aCgifi9QMlJYN19tc19vdXRwdXQiKQppZighZGlyLmV4aXN0cyhPVVRQVVRfRElSKSkgZGlyLmNyZWF0ZShPVVRQVVRfRElSKQpgYGAKCgpgYGB7cn0KZCA8LSByZWFkLmNzdigiLi4vZGF0YS9TVEFScXVhbnRNb2RlX2dlbmVjb3VudHNfYWxsLmNzdiIsIHJvdy5uYW1lcyA9IDEpCmdlbmVzIDwtIHJvd25hbWVzKGQpCgpzYW1wbGVzIDwtIGNvbG5hbWVzKGQpCmNlbGxfbGluZXMgPC0gZmFjdG9yKHNhcHBseShzdHJzcGxpdChzYW1wbGVzLCAiXyIpLCAiW1siLCAxKSkKY29uZHMgPC0gZmFjdG9yKGlmZWxzZShzYXBwbHkoc3Ryc3BsaXQoc2FtcGxlcywgIl8iKSwgIltbIiwgMik9PSI4ZGF5IiwidG9sZXJhbnQiLCJuYWl2ZSIpKQpyZXBzIDwtIGZhY3Rvcihnc3ViKCJyZXAiLCIiLHNhcHBseShzdHJzcGxpdChzYW1wbGVzLCAiXyIpLCAiW1siLCAzKSkpCmNvbGRhdCA8LSBkYXRhLmZyYW1lKGNlbGxfbGluZT1jZWxsX2xpbmVzLGNvbmRpdGlvbj1jb25kcyxyZXA9cmVwcykKY29sZGF0JGdyb3VwIDwtIGZhY3RvcihwYXN0ZShjb2xkYXQkY2VsbF9saW5lLGNvbGRhdCRjb25kaXRpb24sc2VwPSJfIikpCgpsb3dnZW5lcyA8LSByb3duYW1lcyhkW2FwcGx5KGQsMSxmdW5jdGlvbih4KSBhbGwoeDwxMCkpLF0pCmBgYAoKIyMjIE1ha2UgREVTZXEgZGF0YXNldApgYGB7cn0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbGRhdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBjZWxsX2xpbmUgKyBjb25kaXRpb24pCgpgYGAKCiMjIyBTZXQgcmVmZXJlbmNlIGNvbmRpdGlvbgpVc2luZyBkcnVnLXRvbGVyYW50IEEzNzUgY2VsbHMgYXMgcmVmZXJlbmNlLgoKTWFpbnRhaW5pbmcgbG93LWV4cHJlc3NlZCBnZW5lcyByZWR1Y2VzIHRoZSBpbXBhY3Qgb2YgY2VsbCBsaW5lLXNwZWNpZmljIGRpZmZlcmVuY2VzIGluIGdlbmUgZXhwcmVzc2lvbi4gCmBgYHtyfQpkZHMkY2VsbF9saW5lIDwtIHJlbGV2ZWwoZGRzJGNlbGxfbGluZSwgcmVmID0gIkEzNzUiKQpkZHMkY29uZGl0aW9uIDwtIHJlbGV2ZWwoZGRzJGNvbmRpdGlvbiwgcmVmID0gInRvbGVyYW50IikKCmRkcyA8LSBERVNlcShkZHMpCmBgYAoKIyMjIE5vcm1hbGl6ZWQgY291bnRzCmBgYHtyfQpkZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHMpCm5vcm1hbGl6ZWRfY291bnRzIDwtIGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQ9VFJVRSkKYGBgCgojIyMgVmFyaWFuY2Ugc3RhYmlsaXphdGlvbiBmb3IgUENBCmBgYHtyfQpyZXMgPC0gcmVzdWx0cyhkZHMpCnZzZCA8LSB2c3QoZGRzLCBibGluZD1GQUxTRSkKYGBgCgojIyMgJSBvZiBnZW5lcyB0aGF0IGFyZSBib3RoIGBpcy5uYShwYWRqKWAgYW5kIGluIGBsb3dnZW5lc2AKTWlzc2luZyB2YWx1ZXMgZm9yIHBhZGogYXJlIGhpZ2hseSBlbnJpY2hlZCBpbiBsb3ctZXhwcmVzc2VkIGdlbmVzLgpgYGB7cn0KY2F0KHBhc3RlKHNpZ25pZihsZW5ndGgod2hpY2gocm93bmFtZXMocmVzKVt3aGljaChpcy5uYShyZXMkcGFkaikpXSAlaW4lIGxvd2dlbmVzKSkvbGVuZ3RoKHJvd25hbWVzKHJlcylbd2hpY2goaXMubmEocmVzJHBhZGopKV0pKjEwMCw0KSwiJSBvZiBnZW5lcyB3aXRoIHBhZGogPT0gTkEgYXJlIGluIGxvd2dlbmVzIikpCmBgYAoKIyMjIFBDQSBvZiB2YXJpYW5jZS1zdGFiaWxpemVkIGRhdGEKYGBge3J9CnBjYURhdGEgPC0gcGxvdFBDQSh2c2QsIGludGdyb3VwPWMoImNvbmRpdGlvbiIsICJjZWxsX2xpbmUiKSwgcmV0dXJuRGF0YT1UUlVFKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhRGF0YSwgInBlcmNlbnRWYXIiKSkKZ2dwbG90KHBjYURhdGEsIGFlcyhQQzEsIFBDMiwgY29sb3I9Y29uZGl0aW9uLCBzaGFwZT1jZWxsX2xpbmUpKSArCiAgZ2VvbV9wb2ludChzaXplPTMpICsKICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKSArIAogIGNvb3JkX2ZpeGVkKCkKYGBgCgoKIyMjIFNwZWNpZmljYWxseSBleGFtaW5lIHRvbGVyYW50IHZzIG5haXZlCklnbm9yaW5nIGNlbGwgbGluZS1zcGVjaWZpYyBkaWZmZXJlbmNlcy4gCmBgYHtyfQpyZXNfdG9sZXJhbnQgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0PWMoImNvbmRpdGlvbiIsInRvbGVyYW50IiwibmFpdmUiKSkKcmVzX3RvbGVyYW50IDwtIHJlc190b2xlcmFudFtvcmRlcihyZXNfdG9sZXJhbnQkcGFkaiksXQpyZXNfdG9sZXJhbnQgPC0gcmVzX3RvbGVyYW50WyFpcy5uYShyZXNfdG9sZXJhbnQkcGFkaiksXQpzdW1tYXJ5KHJlc190b2xlcmFudCkKYGBgCgojIyBWb2xjYW5vIHBsb3Qgb2YgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFfQpnZ3AgPC0gRW5oYW5jZWRWb2xjYW5vKHJlc190b2xlcmFudCwKICAgIGxhYiA9IHJvd25hbWVzKHJlc190b2xlcmFudCksCiAgICB4ID0gJ2xvZzJGb2xkQ2hhbmdlJywKICAgIHkgPSAncHZhbHVlJywKICAgIHBDdXRvZmYgPSAxZS0wMywKICAgIHBDdXRvZmZDb2wgPSAicGFkaiIsCiAgICB0aXRsZSA9ICJEcnVnLXRvbGVyYW50IHZzIG5haXZlIiwKICAgIHN1YnRpdGxlID0gYnF1b3RlKGl0YWxpYygiYWxsIHNhbXBsZXMsIGFsbCBnZW5lcyIpKSwKICAgIGF4aXNMYWJTaXplPTksCiAgICBsYWJTaXplPTEuNSwgbGVnZW5kTGFiU2l6ZT03LCBsZWdlbmRJY29uU2l6ZT0xLCBwb2ludFNpemU9MSwKICAgIHRpdGxlTGFiU2l6ZT0xMiwgc3VidGl0bGVMYWJTaXplPTEwLCBjYXB0aW9uTGFiU2l6ZT0xMCwKICAgIHNoYXBlID0gMjAsCiAgICBjb2wgPSBjKCJncmV5MzAiLCAiZ3JleTYwIiwgIm9yYW5nZSIsICJyZWQyIiksCiAgICBtYXgub3ZlcmxhcHMgPSAyMCwKICAgIGJvcmRlcldpZHRoID0gMC42LAogICAgZW5jaXJjbGVTaXplID0gMQogICAgKQpnZ3AKYGBgCgpgYGB7cn0KbXlsYWJlbHMgPC0gYygiQUJDQjUiLCJNRVJUSyIsIk5HRlIiLCJBWEwiLCJFR0ZSIiwiVFlSUDEiLCJEQ1QiLCJFRE5SQiIsIlBQQVJHQzFBIiwKICAgICAgICAgICAgICAiVFJQTTEiLCJUUlBNOCIsIlAyUlg0IiwiUDJSWDciLCJDQUNOQTFDIiwiUEFOWDIiLCJDQUNOQTFEIiwKICAgICAgICAgICAgICAiVFJQQzQiLCJORkFUQzQiLAogICAgICAgICAgICAgICJTTEMyNEEyIiwiU1BSWTQiLCJFUkVHIiwiTUlURiIsCiAgICAgICAgICAgICAgIkVUVjUiLCJFVFY0IiwiU1BSWTEiLAogICAgICAgICAgICAgICJDQ05EMiIsIkNYQ0wxMiIpCmBgYAoKCiMjIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSBTNwpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD01LCB3YXJuaW5nPUZBTFNFfQpnZ3AgPC0gRW5oYW5jZWRWb2xjYW5vKHJlc190b2xlcmFudCwKICAgIGxhYiA9IHJvd25hbWVzKHJlc190b2xlcmFudCksCiAgICBzZWxlY3RMYWIgPSBteWxhYmVscywKICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgeSA9ICdwdmFsdWUnLAogICAgeGxpbT1jKC02LDkpLAogICAgeWxpbT1jKDAsNzUpLCAgICAKICAgIHBDdXRvZmYgPSAxZS0wMywKICAgIHBDdXRvZmZDb2wgPSAicGFkaiIsCiAgICB0aXRsZSA9ICJEcnVnLXRvbGVyYW50IHZzIG5haXZlIiwKICAgIHN1YnRpdGxlID0gYnF1b3RlKGl0YWxpYygiYWxsIHNhbXBsZXMiKSksCiAgICBheGlzTGFiU2l6ZT05LAogICAgbGFiU2l6ZT0xLjI1LCBsZWdlbmRMYWJTaXplPTcsIGxlZ2VuZEljb25TaXplPTEsIHBvaW50U2l6ZT0xLAogICAgdGl0bGVMYWJTaXplPTEyLCBzdWJ0aXRsZUxhYlNpemU9MTAsIGNhcHRpb25MYWJTaXplPTEwLAogICAgc2hhcGUgPSAyMCwKICAgIGNvbCA9IGMoImdyZXkzMCIsICJncmV5NjAiLCAib3JhbmdlIiwgInJlZDIiKSwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgIHdpZHRoQ29ubmVjdG9ycyA9IC41LAogICAgdHlwZUNvbm5lY3RvcnMgPSAiY2xvc2VkIiwKICAgIGVuZHNDb25uZWN0b3JzID0gImxhc3QiLAogICAgYm94ZWRMYWJlbHMgPSBUUlVFLAogICAgbWF4Lm92ZXJsYXBzID0gMjAsCiAgICBib3JkZXJXaWR0aCA9IDAuNiwKICAgIGVuY2lyY2xlU2l6ZSA9IDEKICAgICkKZ2dwCmZuIDwtIGZpbGUucGF0aChPVVRQVVRfRElSLCJWb2xjYW5vX2F2Zy5wZGYiKQppZihPVkVSV1JJVEUgfCAhZmlsZS5leGlzdHMoZm4pKQogIGdnc2F2ZShmaWxlPWZuLCBkZXZpY2U9InBkZiIsIHdpZHRoPTQsIGhlaWdodD01LCB1bml0cz0iaW4iKQpgYGAKCiMjIyBHZW5lIGVucmljaG1lbnQgb250b2xvZ3kgYW5hbHlzaXMKVXNpbmcgQmVuamFtaW5pLUhvY2hiZXJnLWFkanVzdGVkIHAtdmFsdWUgb2YgMC4wNSBhbmQgMS41LWZvbGQgY2hhbmdlIG9mIGV4cHJlc3Npb24gYXMgY3V0b2Zmcy4KYGBge3J9CnVwcmVnX2dlbmVzIDwtIHN1YnNldChyZXNfdG9sZXJhbnQsIHBhZGo8MC4wNSAmIGxvZzJGb2xkQ2hhbmdlPjAuNTg0OTYyNSkKZG93bnJlZ19nZW5lcyA8LXN1YnNldChyZXNfdG9sZXJhbnQsIHBhZGo8MC4wNSAmIGxvZzJGb2xkQ2hhbmdlPCgtMC41ODQ5NjI1KSkKCmdlbmVMaXN0X3VwIDwtIGFzLnZlY3Rvcih1cHJlZ19nZW5lcyRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfdXApIDwtIHJvd25hbWVzKHVwcmVnX2dlbmVzKQpnZW5lTGlzdF9kb3duIDwtIGFzLnZlY3Rvcihkb3ducmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKQpuYW1lcyhnZW5lTGlzdF9kb3duKSA8LSByb3duYW1lcyhkb3ducmVnX2dlbmVzKQoKZ2VuZXNfdXAgPC0gYXMudmVjdG9yKHJvd25hbWVzKHVwcmVnX2dlbmVzKSkKZ2VuZXNfZG93biA8LSBhcy52ZWN0b3Iocm93bmFtZXMoZG93bnJlZ19nZW5lcykpCmBgYAoKIyMjIFVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgIHRvIHBlcmZvcm0gZ2VuZSBvbnRvbG9neSBlbnJpY2htZW50IGFuYWx5c2lzCmh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL2NsdXN0ZXJQcm9maWxlci9pbnN0L2RvYy9jbHVzdGVyUHJvZmlsZXIuaHRtbAoKYGBge3J9Cmdnb191cCA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSAgPSBnZW5lc191cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlUeXBlICA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgID0gIk1GIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCAgICA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQpnZ29fdXAgPC0gYXMuZGF0YS5mcmFtZShnZ29fdXApCmdnb191cCA8LSBnZ29fdXBbb3JkZXIoLWdnb191cCRDb3VudCksXSAKZWdvX2dlbmVzVXAgPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lICA9IGdlbmVzX3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlUeXBlICA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICAgICAgID0gIk1GIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiAgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlICAgICAgPSBUUlVFKQpgYGAKCmBgYHtyfQpjbHVzdGVyUHJvZmlsZXI6OmRvdHBsb3QoZWdvX2dlbmVzVXApICsgCiAgZ2d0aXRsZSgiR08gT3Zlci1yZXByZXNlbnRhdGlvbiBVcHJlZ3VsYXRlZCBHZW5lcyIpICsKICBsYWJzKHg9IkdlbmUgUmF0aW8iLCB5PSJHTyBUZXJtcyIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgsZmFjZT0iYm9sZCIpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTksIGZhY2U9ImJvbGQiKSkKCmZuIDwtIGZpbGUucGF0aChPVVRQVVRfRElSLCJHT191cHJlZy5wZGYiKQppZihPVkVSV1JJVEUgfCAhZmlsZS5leGlzdHMoZm4pKQogIGdnc2F2ZShmaWxlPWZuLCBkZXZpY2U9InBkZiIsIHdpZHRoPTYsIGhlaWdodD00LCB1bml0cz0iaW4iKQpgYGAKCmBgYHtyfQpnZ29fZG93biA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSAgPSBnZW5lc19kb3duLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ICAgICAgPSAiTUYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsICAgID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZSA9IFRSVUUpCmdnb19kb3duIDwtIGFzLmRhdGEuZnJhbWUoZ2dvX2Rvd24pCmdnb19kb3duIDwtIGdnb19kb3duW29yZGVyKC1nZ29fZG93biRDb3VudCksXSAKZWdvX2dlbmVzRG93biA8LSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKGdlbmUgID0gZ2VuZXNfZG93biwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgICAgICAgICA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSAgICAgICA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICAgICAgID0gIk1GIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiAgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlICAgICAgPSBUUlVFKQpgYGAKCmBgYHtyfQpjbHVzdGVyUHJvZmlsZXI6OmRvdHBsb3QoZWdvX2dlbmVzRG93bikgKyAKICBnZ3RpdGxlKCJHTyBPdmVyLXJlcHJlc2VudGF0aW9uIERvd25yZWd1bGF0ZWQgR2VuZXMiKSArCiAgbGFicyh4PSJHZW5lIFJhdGlvIiwgeT0iR08gVGVybXMiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04LGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT05LCBmYWNlPSJib2xkIikpCmBgYAoKCgpgYGB7cn0KdXBnZW5lc19maWx0ZXJlZCA8LSBhcy5kYXRhLmZyYW1lKHVwcmVnX2dlbmVzW29yZGVyKHVwcmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjcmVhc2luZz1UUlVFKSxdKQoKdXBnZW5lc19maWx0ZXJlZCA8LSBzdWJzZXQodXBnZW5lc19maWx0ZXJlZCwgbG9nMkZvbGRDaGFuZ2U+MS41ICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkaiA8IDFlLTQgJiBiYXNlTWVhbiA+IDIwMCkKCiMjIyBFeGNsdWRlIExJTkMgKGxvbmcgbm9uY29kaW5nIFJOQXMpIGFuZCBMT0MgKHVuYW5ub3RhdGVkKSBnZW5lcwp1cGdlbmVzX2ZpbHRlcmVkIDwtIHN1YnNldCh1cGdlbmVzX2ZpbHRlcmVkLCAhZ3JlcGwoIl5MW09JXVtDTl0iLHJvd25hbWVzKHVwZ2VuZXNfZmlsdGVyZWQpKSkKCnVwZ2VuZXNfZmlsdGVyZWQKYGBgCgoKQ2FsY2l1bSBpb24gdHJhbnNwb3J0IGdlbmUgb250b2xvZ2llczogIApodHRwczovL3d3dy5pbmZvcm1hdGljcy5qYXgub3JnL3ZvY2FiL2dlbmVfb250b2xvZ3kvR086MDAwNjgxNgoKY2FsY2l1bSBpb24gdHJhbnNtZW1icmFuZSB0cmFuc3BvcnQgIApHTzowMDcwNTg4ICAKCkFtaW5vIGFjaWQgdHJhbnNwb3J0ZXIgCkdPOjAwMDY4NjUgIAoKbW9ub2F0b21pYyBjYXRpb24gY2hhbm5lbCBhY3Rpdml0eQpHTzowMDA1MjYxCgpgYGB7cn0KZ29fZW5yaWNoZWQyIDwtIHN0cnNwbGl0KGVnb19nZW5lc1VwQHJlc3VsdFsiR086MDAwNTI2MSIsImdlbmVJRCJdLCIvIikKbmFtZXMoZ29fZW5yaWNoZWQyKSA8LSAiR086MDAwNTI2MSIKCmdvX2xhYmVscyA8LSB1bmxpc3QoZ29fZW5yaWNoZWQyW1sxXV0pCnggPC0gcmVzX3RvbGVyYW50W2dvX2xhYmVscyxjKCJsb2cyRm9sZENoYW5nZSIsInBhZGoiKV0KeCA8LSB4W29yZGVyKHgkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmc9VFJVRSksXQpnb19sYWJlbHMgPC0gcm93bmFtZXMoeCkKCmdvX2xhYmVsczIgPC0gYygiS0NORDMiLCJLQ05RMyIsIktDTkIxIiwiUDJSWDciLCJLQ05KMTMiLCJQMlJYMSIsIlRNRU02M0MiLCJDQUNOQTFEIiwiQ0FDTkExQyIpCmBgYAoKIyMjIFN1cHBsZW1lbnRhcnkgRmlndXJlIFM4CgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02LCB3YXJuaW5nPUZBTFNFfQpnZ3AgPC0gRW5oYW5jZWRWb2xjYW5vKHJlc190b2xlcmFudCwKICAgIGxhYiA9IHJvd25hbWVzKHJlc190b2xlcmFudCksCiAgICBzZWxlY3RMYWIgPSBnb19sYWJlbHMsCiAgICB0aXRsZSA9ICJHZW5lcyBhc3NvY2lhdGVkIHdpdGggbW9ub2F0b21pYyBjYXRpb24gY2hhbm5lbCBhY3Rpdml0eSIsCiAgICBzdWJ0aXRsZSA9IGJxdW90ZShpdGFsaWMoR086MDAwNTI2MSkpLAogICAgY2FwdGlvbiA9IHBhc3RlMCgidG90YWwgPSAiLCBucm93KHJlc190b2xlcmFudCksICIgZ2VuZXMiKSwKICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgeSA9ICdwdmFsdWUnLAogICAgcEN1dG9mZiA9IDFlLTAyLAogICAgcEN1dG9mZkNvbCA9ICJwYWRqIiwKICAgIHhsaW09YygtMSw2KSwKICAgIHlsaW09YygwLDYwKSwKICAgIGF4aXNMYWJTaXplPTksCiAgICBsYWJTaXplPTEuMjUsIGxlZ2VuZExhYlNpemU9NywgbGVnZW5kSWNvblNpemU9MSwgcG9pbnRTaXplPTEsCiAgICB0aXRsZUxhYlNpemU9MTIsIHN1YnRpdGxlTGFiU2l6ZT0xMCwgY2FwdGlvbkxhYlNpemU9MTAsCiAgICBzaGFwZSA9IDIwLAogICAgY29sID0gYygiZ3JleTMwIiwgImdyZXk2MCIsICJvcmFuZ2UiLCAicmVkMiIpLAogICAgZHJhd0Nvbm5lY3RvcnMgPSBUUlVFLAogICAgd2lkdGhDb25uZWN0b3JzID0gLjUsCiAgICB0eXBlQ29ubmVjdG9ycyA9ICJjbG9zZWQiLAogICAgZW5kc0Nvbm5lY3RvcnMgPSAibGFzdCIsCiAgICBib3hlZExhYmVscyA9IFRSVUUsCiAgICBtYXgub3ZlcmxhcHMgPSAyMCwKICAgIGJvcmRlcldpZHRoID0gMC42LAogICAgZW5jaXJjbGVTaXplID0gMQogICAgKQpnZ3AKCmZuIDwtIGZpbGUucGF0aChPVVRQVVRfRElSLCJWb2xjYW5vX2F2Z19HTy01MjYxLnBkZiIpCmlmKE9WRVJXUklURSB8ICFmaWxlLmV4aXN0cyhmbikpCiAgZ2dzYXZlKGZpbGU9Zm4sIGRldmljZT0icGRmIiwgd2lkdGg9NiwgaGVpZ2h0PTQsIHVuaXRzPSJpbiIpCmBgYAoKIyMjIFBsb3R0aW5nIGZ1bmN0aW9uIGZvciBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzIGFjcm9zcyBjZWxsIGxpbmUgZGF0YSAoZHJ1Zy10b2xlcmFudCB2cyBuYWl2ZSkKYGBge3J9CnBsb3RfbmFpdmVfdl90b2wgPC0gZnVuY3Rpb24oZ2VuZSwgeWw9YygtMiwxOCkpewogIGdlbmUgPC0gZ2VuZVsxXSAjIHVzZSBvbmx5IGZpcnN0IGl0ZW0KICB4IDwtIGNiaW5kKGNvbGRhdCxnZW5lPWFzLmludGVnZXIobm9ybWFsaXplZF9jb3VudHNbZ2VuZSxdKSkKICBnIDwtIGdncGxvdCh4LCBhZXMoY2VsbF9saW5lLCBsb2cyKGdlbmUpLCBjb2xvcj1jb25kaXRpb24sIHNoYXBlPWNvbmRpdGlvbikpICsKICAgIGdlb21faml0dGVyKHdpZHRoID0gLjA3NSwgc2l6ZT0yKSArIAogICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKyAKICAgIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTApLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSArIAogICAgbGFicyh5PSJsb2cyKG5vcm1hbGl6ZWQgY291bnRzKSIsIHg9IiIsIHRpdGxlPWdlbmUpICsgCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPXlsLCBicmVha3M9c2VxKHlsWzFdLHlsWzJdLDIpKQogIGcKfQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xLjV9CnBsb3RfbmFpdmVfdl90b2woIkFUUDFBMSIsIHlsPWMoMTQsMTkpKQpwbG90X25haXZlX3ZfdG9sKCJQMlJYNyIpCmBgYAoKCmBgYHtyfQpkb3ducmVnX2dlbmVzWyJJVFBSMiIsXQpgYGAKCgojIyMgU3VwcGxlbWVudGFyeSBGaWd1cmUgUzYKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MS41fQpwbG90X25haXZlX3ZfdG9sKCJJVFBSMiIsIHlsPWMoOCwxMykpCnBsb3RfbmFpdmVfdl90b2woIklUUFIzIix5bD1jKDExLDE1KSkKYGBgCgoKYGBge3J9CmRvd25yZWdfZ2VuZXNbIkNBTEIyIixdCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MS41fQpwIDwtIGxpc3QocGxvdF9uYWl2ZV92X3RvbCgiQ0FDTkExQyIseWw9YygtMiw4KSksCiAgICAgICAgICBwbG90X25haXZlX3ZfdG9sKCJDQUNOQTFEIix5bD1jKDIsMTApKSwKICAgICAgICAgIHBsb3RfbmFpdmVfdl90b2woIlAyUlg0Iix5bD1jKDQsMTQpKSwKICAgICAgICAgIHBsb3RfbmFpdmVfdl90b2woIlAyUlgxIiwgeWw9YygtMiw4KSksCiAgICAgICAgICBwbG90X25haXZlX3ZfdG9sKCJQMlJYNyIsIHlsPWMoMiwxNikpLAogICAgICAgICAgcGxvdF9uYWl2ZV92X3RvbCgiSVRQUjIiLHlsPWMoOCwxMykpLAogICAgICAgICAgcGxvdF9uYWl2ZV92X3RvbCgiQ0FMQjIiLCB5bD1jKC0yLDEyKSksCiAgICAgICAgICBwbG90X25haXZlX3ZfdG9sKCJBVFAyQjIiLCB5bD1jKC0yLDgpKSwKICAgICAgICAgIHBsb3RfbmFpdmVfdl90b2woIk5HRlIiLCB5bD1jKC0yLDE4KSkpCnAKYGBgCgojIyMgTWVsYW5vYmxhc3QgZ2VuZXMKClRob21hcywgQS4gSi4gJiBFcmlja3NvbiwgQy4gQS4gVGhlIG1ha2luZyBvZiBhIG1lbGFub2N5dGU6IHRoZSBzcGVjaWZpY2F0aW9uIG9mIG1lbGFub2JsYXN0cyBmcm9tIHRoZSBuZXVyYWwgY3Jlc3QuIFBpZ21lbnQgQ2VsbCBNZWxhbm9tYSBSZXMgMjEsIDU5OOKAkzYxMCAoMjAwOCkuICAKCk5PVEU6IGFsbCBhcmUgaGlnaCBmb3IgU09YMTAsIFBBWDMgYW5kIE1JVEYgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xLjV9CiMgbmV1cmFsX2NyZXN0X2dlbmVzIDwtIGMoJ05HRlInLCAnQVFQMScsICdHRlJBMicsICdMMUNBTScsICdTTElUUks2JywgJ1JYUkcnLCdTT1gxMCcpCm1lbGFub2JsYXN0X2dlbmVzIDwtIGMoJ1NPWDEwJywnTUlURicsJ1BBWDMnLCdGT1hEMycsJ0tJVCcsJ0RDVCcsJ0VETlJCJywnVFlSUDEnKQoKcCA8LSBsYXBwbHkobWVsYW5vYmxhc3RfZ2VuZXMsIGZ1bmN0aW9uKHgpIHBsb3RfbmFpdmVfdl90b2woeCwgeWw9YygtNCwyMCkpKQpwCmBgYAojIyMgTUFQM0stcmVsYXRlZCBnZW5lcwpBbHNvIGluY2x1ZGluZyBzY2FmZm9sZCBwcm90ZWlucyBhbmQgUkFGIGFuZCBSQVMgaXNvZm9ybXMuICAKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEuNX0KbWFwM2tfZ2VuZXMgPC0gcm93bmFtZXMoZGRzKVtncmVwKCdNQVAzSycscm93bmFtZXMoZGRzKSldCm1hcDNrX2dlbmVzIDwtIG1hcDNrX2dlbmVzWyFncmVwbCgiLSIsbWFwM2tfZ2VuZXMpXQptYXAza19nZW5lcyA8LSBjKG1hcDNrX2dlbmVzLGMoJ0FSQUYnLCdCUkFGJywnUkFGMScsJ1JJVDEnLCdSSVQyJywnU0hPQzInLCdLUkFTJywnTlJBUycsJ0hSQVMnKSkKCnAgPC0gbGFwcGx5KG1hcDNrX2dlbmVzLCBmdW5jdGlvbih4KSBwbG90X25haXZlX3ZfdG9sKHgsIHlsPWMoLTQsMjApKSkKcAoKYGBgCgo=